package com.beta.backend.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.beta.backend.gateway.config.GatewayCfgBean;
import com.beta.backend.gateway.utils.JwtUtil;
import com.beta.platform.rest.auth.LoginAccountDetail;
import com.beta.platform.rest.auth.ThirdUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.UUID;

/**
 * @description  授权认证过滤器，用于校验用户信息，适用于服务端授权
 * @author: zdwang
 * @date 2021/12/2 10:31
 */
@Slf4j
public class ServerAuthGatewayFilter extends AbstractGatewayFilter implements GatewayFilter, Ordered {

    private GatewayCfgBean gatewayCfgBean;


    public ServerAuthGatewayFilter(GatewayCfgBean gatewayCfgBean) {
        this.gatewayCfgBean = gatewayCfgBean;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1、从Cookie中获取bjwt
        ServerHttpRequest req = exchange.getRequest();
        HttpCookie bjwtCookie = req.getCookies().getFirst(BJWT);
        if (bjwtCookie == null) {
            log.warn("ServerAuth bjwtCookie 为空，需要重新授权");
            return sendToAuth(exchange);
        }

        String bjwt = bjwtCookie.getValue();
        if (StringUtils.isBlank(bjwt)) {
            log.warn("ServerAuth 客户端请求业务接口未完成认证，不存在 {}", BJWT);
            return sendToAuth(exchange);
        }

        Map<String, String> map;
        try {
            // 2、解析bjwt
            map = JwtUtil.decrypt(gatewayCfgBean.getJwtSecret(), bjwt);

            // 3、判断token里面是否有用户信息
            if (CollectionUtils.isEmpty(map) || !map.containsKey(USERID)) {
                log.warn("ServerAuth 客户端请求业务接口未完成认证，JwtUtil.decrypt(bjwt) = {}", JSON.toJSONString(map));
                return methodNotAllowed(exchange);
            }

        } catch (TokenExpiredException e) {
            return sendToAuth(exchange);
        } catch (JWTDecodeException e) {
            log.error("ServerAuth BJWT-非法：{}", e.getMessage(), e);
            return methodNotAllowed(exchange);
        } catch (Exception e){
            log.error("ServerAuth BJWT-解析异常：{}", e.getMessage(), e);
            return methodNotAllowed(exchange);
        }

        String userId = map.get(USERID);

        // 4、封装用户信息
        LoginAccountDetail accountDetail = new LoginAccountDetail();
        ThirdUserInfo userInfo = new ThirdUserInfo();
        userInfo.setUserId(userId);
        accountDetail.setThirdUserInfo(userInfo);

        // 5、将用户信息放入请求中
        ServerHttpRequest.Builder requestBuilder = req.mutate();
        requestBuilder.header(USER_INFO, Base64.getEncoder().encodeToString(JSON.toJSONString(accountDetail).getBytes(StandardCharsets.UTF_8)));
        ServerHttpRequest serverHttpRequest = requestBuilder.build();

        // 6、继续后续调用
        return chain.filter(exchange.mutate().request(serverHttpRequest).build());
    }

    @Override
    public int getOrder() {
        return 15;
    }

    /**
     * 发起授权，将请求转发到授权模块
     * @param exchange
     * @return
     */
    private Mono<Void> sendToAuth(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        // 获取带参数的请求路径
        String path = request.getPath().value();
        // 获取query参数
        String query = request.getURI().getQuery();
        // 获取跳转地址（目标地址）
        String returnUrl = gatewayCfgBean.getGatewayBaseUrl() + path;
        if (StringUtils.isNotEmpty(query)) {
            returnUrl = returnUrl + "?" + query;
        }

        // 授权请求唯一标识
        String ustate = UUID.randomUUID().toString();
        // 授权接口地址
        String authUrl = gatewayCfgBean.getAuthUrl();

        try {
            authUrl = authUrl + "?ustate=" + ustate + "&writeCookie=true" + "&returnUrl=" + URLEncoder.encode(returnUrl, "UTF-8");
            log.info("服务端授权地址：{}", authUrl);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalCharsetNameException("UTF-8");
        }
        // 302重定向到授权地址
        exchange.getResponse().setStatusCode(HttpStatus.FOUND);
        exchange.getResponse().getHeaders().setLocation(URI.create(authUrl));

        return exchange.getResponse().setComplete();
    }

}

